#include "H264Decoder.h"
#include "common.h"

CH264Decoder::CH264Decoder(int nIndex, DecodeDataNotify *pNotify)
	: m_bExit(false)
	, m_nIndex(nIndex)
	, m_pNotify(pNotify)
	, m_nSrcWidth(0)
	, m_nSrcHeight(0)
	, m_pOutBuffer(NULL)
	, m_pImg_convert_ctx(NULL)
	, m_nFrameRate(0)
{
	InitDecoder();

	m_nDstWidth = 1920;
	m_nDstHeigth = 1080;

	StartThread();
}

CH264Decoder::~CH264Decoder(void)
{
	m_bExit = true;

	WaitForStop();

	CAutoLock autoLock(&m_lockRecvData);
	while (m_lstRecvData.size())
	{
		LPRecvDataInfo pRecvData = NULL;
			
		pRecvData = (LPRecvDataInfo)m_lstRecvData.front();
		m_lstRecvData.pop_front();

		DelRecvData(pRecvData);		
	}

	avcodec_free_frame(&m_pFrame);
	avcodec_free_frame(&m_pYUVFrame);

	if (m_pAvc)
	{
		if (m_pCode)
		{
			avcodec_close(m_pAvc);
			m_pCode = NULL;
		}
		av_free(m_pAvc);
		m_pAvc = NULL;
	}

	if (m_pOutBuffer)
	{
		delete m_pOutBuffer;
		m_pOutBuffer = NULL;
	}

	if (m_pImg_convert_ctx)
	{
		sws_freeContext(m_pImg_convert_ctx);
		m_pImg_convert_ctx = NULL;
	}
}

void CH264Decoder::PutRecvData(RecvDataInfo *pRecvData)
{
	CAutoLock autoLock(&m_lockRecvData);
	m_lstRecvData.push_back(pRecvData);
}

void CH264Decoder::SetVideoParam(int nWidth, int nHeigth)
{
	m_nDstWidth = nWidth;
	m_nDstHeigth = nHeigth;
}

void CH264Decoder::ThreadProcMain(void)
{
	while (!m_bExit)
	{
		while (m_lstRecvData.size())
		{
			LPRecvDataInfo pRecvData = NULL;

			{
				CAutoLock autoLock(&m_lockRecvData);
				pRecvData = (LPRecvDataInfo)m_lstRecvData.front();
				m_lstRecvData.pop_front();
			}

			if (pRecvData)
			{
				DecoderData(pRecvData->pData, pRecvData->nMsgLen);

				DelRecvData(pRecvData);
			}	

			if (m_bExit)
				return;
		}

		my_sleep(5);
	}
}

bool CH264Decoder::InitDecoder(void)
{
	avcodec_register_all();

	av_init_packet(&m_avPkt);

	m_pCode = avcodec_find_decoder(CODEC_ID_H264);
	if (!m_pCode)
		return false;

	m_pAvc = avcodec_alloc_context3(m_pCode);
	if (!m_pAvc)
		return false;

	if (avcodec_open2(m_pAvc,m_pCode,NULL) < 0)
		return false;

	m_pFrame = avcodec_alloc_frame();
	m_pYUVFrame = avcodec_alloc_frame();	

	return true;
}

int CH264Decoder::DecoderData(char *pData, int nDataLen)
{
	int nRet = 0;
	int nTmp;
	m_avPkt.data = (uint8_t *)pData;
	m_avPkt.size = nDataLen;

 	nRet = avcodec_decode_video2(m_pAvc,m_pFrame,&nTmp,&m_avPkt);
	if (nTmp > 0)
	{
		if ((m_pFrame->width != m_nSrcWidth) || (m_pFrame->height != m_nSrcHeight))
		{
			if (m_pOutBuffer)
			{
				delete m_pOutBuffer;
				m_pOutBuffer = NULL;
			}

			m_pOutBuffer = new uint8_t[avpicture_get_size(PIX_FMT_YUV420P,m_pFrame->width, m_pFrame->height)];
			avpicture_fill((AVPicture *)m_pYUVFrame, m_pOutBuffer, PIX_FMT_YUV420P, m_pFrame->width, m_pFrame->height);
		
			m_nSrcWidth = m_pFrame->width;
			m_nSrcHeight = m_pFrame->height;

			if (m_pImg_convert_ctx)
			{
				sws_freeContext(m_pImg_convert_ctx);
				m_pImg_convert_ctx = NULL;
			}

			m_pImg_convert_ctx = sws_getContext(m_pAvc->width,m_pAvc->height,m_pAvc->pix_fmt,m_nSrcWidth,m_nSrcHeight
				,PIX_FMT_YUV420P,SWS_BICUBIC,NULL,NULL,NULL);
		}

		sws_scale(m_pImg_convert_ctx,(const uint8_t* const *)m_pFrame->data,m_pFrame->linesize,0,m_pAvc->height,m_pYUVFrame->data,m_pYUVFrame->linesize);

		m_pYUVFrame->width = m_pFrame->width;
		m_pYUVFrame->height = m_pFrame->height;

		if (m_pNotify)
			m_pNotify->OnDecodeData(m_nIndex, m_nFrameRate, m_pYUVFrame);
	}

	return nRet;
}

void CH264Decoder::DelRecvData(RecvDataInfo *pRecvData)
{
	if (pRecvData)
	{
		if (pRecvData->pData && pRecvData->nMsgLen > 0)
		{
			delete [] pRecvData->pData;
			pRecvData->pData = NULL;
		}

		delete pRecvData;
		pRecvData = NULL;
	}
}
